﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace _01_TCP_服务端
{

    public enum MessageType
    {
        /// <summary>
        /// 文本消息
        /// </summary>
        TextMessage = 0,
        /// <summary>
        /// 心跳消息
        /// </summary>
        Heartbeat = 1,
        /// <summary>
        /// 个人消息
        /// </summary>
        PersonalInformation,
        /// <summary>
        /// 获取服务器分配的IP地址与端口号
        /// </summary>
        IPAndPort
            
    }

    public partial class Frm_Server : Form
    {
        //创建Socket对象
        Socket socketServer = null;
        //创建字典用于存储连接过来的客户端
        private Dictionary<string, Socket> CurrentClient = new Dictionary<string, Socket>();
        //记录客户端的IP地址和端口号还有名称
        public List<PersonInfo> OnlinePerson = new List<PersonInfo>();

        public Frm_Server()
        {
            InitializeComponent();
            //取消跨线程访问控件
            CheckForIllegalCrossThreadCalls = false;
        }
        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void button1_Click(object sender, EventArgs e)
        {
            //创建Socket对象
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                //绑定IP地址和端口号
                socketServer.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text)));
                //开始监听，设置最大监听数量
                socketServer.Listen(10);
                //创建线程，用于接收客户端连接
                Task.Run(CheckListening);
                //提示启动成功
                richTextBox1.SelectionColor = Color.Green;
                richTextBox1.AppendText("启动服务器成功，等待客户端连接...\r\n");
                //按钮不允许再次启动
                button1.Enabled = false;
            }
            catch (Exception ex)
            {
                //错误提示
                richTextBox1.SelectionColor = Color.Red;
                richTextBox1.AppendText("启动服务器失败，原因:" + ex.Message + "\r\n");
            }
        }

        public void CheckListening()
        {
            while (true)
            {
                //调用Accept()函数在引发当前线程的阻塞，直到有一个客户端连接才会解除阻塞。
                Socket socketClient = socketServer.Accept();
                //当收到客户端连接过来的内容的时候，发送消息给客户端，说明分配到的IP地址与端口号
                byte[] bytes = GetBytesByDefault(socketClient.RemoteEndPoint.ToString(), MessageType.IPAndPort);
                socketClient.Send(bytes);
                //将连接过来的客户端存储到字典中
                CurrentClient.Add(socketClient.RemoteEndPoint.ToString(), socketClient);
                //创建线程，用于接收客户端消息
                Task.Run(()=>ReceiveMessage(socketClient));
                //将链接过来的客户端在下拉列表上显示
                comboBox1.Items.Add(socketClient.RemoteEndPoint.ToString());
                //提示客户端连接成功
                richTextBox1.SelectionColor = Color.Green;
                richTextBox1.AppendText("客户端:" + socketClient.RemoteEndPoint.ToString() + "连接成功\r\n");
                
            }
        }
        /// <summary>
        /// 接收客户端发送过来的消息
        /// </summary>
        /// <param name="socket"></param>
        public void ReceiveMessage(Socket socket)
        {
            while (true)
            {
                //创建byte数组
                byte[] bytes = new byte[1024 * 1024 * 2];
                try
                {
                    //接收字符串内容
                    int length = socket.Receive(bytes);//阻塞当前线程，如果没有数据可读，会一直等待。有数组解除阻塞
                    if (length > 0)
                    {
                        //获取第一个标志位
                        MessageType messageType = (MessageType)bytes[0];
                        //判断消息类型
                        switch (messageType)
                        {
                            case MessageType.TextMessage:
                                //将byte数组转换为字符串
                                string message = Encoding.Default.GetString(bytes, 1, length - 1);
                                //将消息显示在文本框中
                                richTextBox1.SelectionColor = Color.Black;
                                richTextBox1.AppendText("客户端:" + socket.LocalEndPoint.ToString() + "发送消息:" + message + "\r\n");
                                break;
                            case MessageType.Heartbeat:
                                break;
                            case MessageType.PersonalInformation:
                                //将byte数组转换为字符串
                                string message1 = Encoding.Default.GetString(bytes, 1, length - 1);
                                //用 | 分割字符串 提取 用户名 与 IP地址(唯一)
                                string[] str = message1.Split('|');
                                //实例化用户信息保存到集合当中
                                PersonInfo info = new PersonInfo(str[1],str[0]);
                                //添加到在线集合
                                OnlinePerson.Add(info);
                                //遍历列表然后添加
                                this.listBox1.Items.Clear();
                                foreach (var item in OnlinePerson)
                                {
                                    this.listBox1.Items.Add(item.Name);
                                }
                                break;
                            default:
                                break;
                        }

                    }
                    //else
                    //{
                    //    //提示没有收到内容
                    //    richTextBox1.SelectionColor = Color.Red;
                    //    richTextBox1.AppendText("客户端:" + socket.RemoteEndPoint.ToString() + "没有发送任何内容\r\n");
                    //}
                }
                catch (Exception ex)
                {
                    //将客户端从字典中移除
                    CurrentClient.Remove(socket.RemoteEndPoint.ToString());
                    //将客户端从下拉列表中移除
                    comboBox1.Items.Remove(socket.RemoteEndPoint.ToString());
                    //提示错误
                    richTextBox1.SelectionColor = Color.Red;
                    richTextBox1.AppendText("客户端:" + socket.RemoteEndPoint.ToString() + "断开连接，原因:" + ex.Message + "\r\n");
                    //退出循环
                    break;
                }
            }
        }

        public byte[] GetBytesByDefault(string info, MessageType messageType)
        {
            //将输入的内容转换为byte数组
            byte[] bytes = Encoding.Default.GetBytes(info);
            //创建新的数组比原有数组长度+1
            byte[] newBytes = new byte[bytes.Length + 1];
            //设置第一个字节为:MessageType.TextMessage
            newBytes[0] = (byte)messageType;
            //将原有数组复制到新数组中,从第二个字节开始
            bytes.CopyTo(newBytes, 1);
            //返回新数组
            return newBytes;
        }

        /// <summary>
        /// 服务端：发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void button3_Click(object sender, EventArgs e)
        {
            //将输入的内容转换为byte数组
            byte[] bytes = Encoding.Default.GetBytes(textBox3.Text);
            //创建新的数组比原有数组长度+1
            byte[] newBytes = new byte[bytes.Length + 1];
            //设置第一个字节为:MessageType.TextMessage
            newBytes[0] = (byte)MessageType.TextMessage;
            //将原有数组复制到新数组中,从第二个字节开始
            bytes.CopyTo(newBytes, 1);
            //获取下拉列表选中的客户端
            string client = comboBox1.SelectedItem.ToString();
            //如果没有选中客户端，提示
            if (string.IsNullOrEmpty(client))
            {
                richTextBox1.SelectionColor = Color.Red;
                richTextBox1.AppendText("请选择客户端\r\n");
                return;
            }
            //获取客户端的socket
            Socket socket = CurrentClient[client];
            //发送消息
            socket.Send(newBytes);

        }
        /// <summary>
        /// 停止服务端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            socketServer.Close();
            richTextBox1.SelectionColor = Color.Red;
            richTextBox1.AppendText("服务端已停止\r\n");
            //按钮可用
            button1.Enabled = true;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();//清空ListBox1
            
            //遍历所有客户端添加到ListBox1集合当中
            foreach (var item in CurrentClient)
            {
                listBox1.Items.Add(item.Key);
            }
            this.button4.Enabled = false;
            //循环想客户端发送，如果发送失败说明已经下线
            Task.Run(() => 
            {
                foreach (var item in CurrentClient.Keys)
                {
                    try
                    {
                        //将输入的内容转换为byte数组
                        byte[] bytes = Encoding.Default.GetBytes(DateTime.Now.ToString());
                        //创建新的数组比原有数组长度+1
                        byte[] newBytes = new byte[bytes.Length + 1];
                        //设置第一个字节为:MessageType.TextMessage
                        newBytes[0] = (byte)MessageType.Heartbeat;
                        //将原有数组复制到新数组中,从第二个字节开始
                        bytes.CopyTo(newBytes, 1);
                        //发送心跳包
                        CurrentClient[item].Send(newBytes);
                    }
                    catch (Exception ex)
                    {
                        //发送失败说明下线了，将其从列表删除，并在消息框中提示下线了
                        CurrentClient.Remove(item);
                        //下拉列表中删除
                        comboBox1.Items.Remove(item);
                        //列表中删除
                        listBox1.Items.Remove(item);
                    }
                    Thread.Sleep(100);
                }

            }).ContinueWith(t=> this.button4.Enabled = true );
        }
    }
}
